Web Icon 实现方案总结
Icon,在界面设计中,具有指代意义的图形符号。在前端开发中,图标往往由 UI 设计给出,然后经前端开发人员在 html 中使用。Icon 的设计和使用在近几年的发展中,也经历了由当初的 img 方案 到现如今的 svg 方案。下文将从 Icon 的发展历程以及到现如今的 svg sprite 技术给出具体的介绍。
Icon 发展历程
初期 img 的方案
小时候,我们都是这样在前端应用一个 icon 的:
- 网上找到相关的图片资源或者由视觉同学给出
- 下载到本地 assets/img 文件夹, 重命名为 index.png
- 页面上使用
<img src="assets/img/index.png" />
,或者直接引用一个远程的图片地址 - 使用 css 控制 img 标签的样式
还未曾长大,有人就说了,孩子,你这样不行啊。页面中要是有很多 Icon 的话岂不请求中都是图片请求。听大人一说,当初的自己吓得抓紧百度了一下。有一篇文章《Best Practices for Speeding Up Your Web Site》,也就是现在大家熟知的雅虎前端性能优化准则。第一条:
Minimize HTTP Requests (减少/最小化 http 请求数)
显然因为Icon的展示,就导致了页面中那么多的 http 请求,有点不合情理,大人说的很对啊。
于是乎,又打开了百度,怎么优雅的使用的icon呢? css sprite ,这是个什么鬼?
CSS Sprites are a means of combining multiple images into a single image file for use on a website, to help with performance.
( CSS Sprites 是为优化性能而将多个图片合并到一个图片在网站中使用的方式。)
有了 css sprite, 前端在使用时,只请求一次图片,通过 css 的 background-img
、 background-position
属性控制显示 icon。很显然,减少了 http 的请求次数,终于又可以开开心心的玩耍了。
然而还没开心多久,蛋疼的事情来了。项目中其他功能需要添加额外的 icon,UI 只能是重新再搞图,如果不会影响之前 icon的位置还好,若是调整了之前icon 的位置,以前写的css又得无奈的改动。
如果每次这样的修改都让 UI 给调整,估计 UI 早早的就疯了。还好社区内很快出现了相应的工具来自动化的完成这些工作。Sprity 一个根据相应配置自动合成 sprite 图的工具,在当时也提供了基于Gulp/Grant 的插件。很显然这种技术在目前已经不怎么实用了,Sprity github 上的项目最近一次提交也已经是2年前的事情了。《CSS Sprites: What They Are, Why They’re Cool, and How To Use Them》 这篇文章中给出了在当时如何使用工具自动生成 sprite 图片的方法。
在当时为了优化性能,还有一种技术 Data URIs,它可以将图片编码后内联于样式表中,避免了额外的 http 请求,同时还能避免配置 background-position
。为优化性能引入的这种技术,可能还存在一定的性能问题,具体可以查看这篇文章给出的论述: 《Data URIs》。
Icon Font
Web Font 的发展得益于 CSS3 的@font-face属性。
允许网页开发者为其网页指定在线字体。 通过这种作者自备字体的方式,@font-face 可以消除对用户电脑字体的依赖。
Icon Font 的思想来自于 Web Font,使用字体的方式设计 Icon。其中阿里巴巴开源的图标库 IconFont 应用广泛。下面以 Iconfont 图标库为例,介绍其使用方法。
登录 IconFont 后可以搜索自己想要的图标并添加至购物车,购物车中的图标可以添加至项目。Iconfont 提供了以项目进行管理图标的功能。项目中的 web 端图标使用方式有三种:Unicode, Font Class, Symbol。
Unicode引用
unicode 是字体在网页端最原始的应用方式,特点是:
- 兼容性最好,支持 ie6+,及所有现代浏览器。
- 支持按字体的方式去动态调整图标大小,颜色等等。
- 但是因为是字体,所以不支持多色。只能使用平台里单色的图标,就算项目里有多色图标也会自动去色。
使用步骤:
第一步:拷贝项目下面生成的 font-face
1 | @font-face {font-family: 'iconfont'; |
第二步:定义使用 iconfont 的样式
1 | .iconfont{ |
第三步:挑选相应图标并获取字体编码,应用于页面
1 | <i class="iconfont">3</i> |
font-class引用
font-class 是 unicode 使用方式的一种变种,主要是解决 unicode 书写不直观,语意不明确的问题。
与 unicode 使用方式相比,具有如下特点:
- 兼容性良好,支持 ie8+,及所有现代浏览器。
- 相比于 unicode 语意明确,书写更直观。可以很容易分辨这个 icon 是什么。
- 因为使用 class 来定义图标,所以当要替换图标时,只需要修改 class 里面的 unicode 引用。
- 不过因为本质上还是使用的字体,所以多色图标还是不支持的。
使用步骤如下:
第一步:拷贝项目下面生成的 fontclass 代码:
1 | //at.alicdn.com/t/font_8d5l8fzk5b87iudi.css |
第二步:挑选相应图标并获取类名,应用于页面
1 | <i class="iconfont icon-xxx"></i> |
symbol 引用
这是一种全新的使用方式,应该说这才是未来的主流,也是平台目前推荐的用法。 这种用法其实是做了一个svg的集合,与上面两种相比具有如下特点:
- 支持多色图标了,不再受单色限制。
- 通过一些技巧,支持像字体那样,通过 font-size,color 来调整样式。
- 兼容性较差,支持 ie9+,及现代浏览器。
- 浏览器渲染 svg 的性能一般,还不如 png。
使用步骤如下:
第一步:拷贝项目下面生成的 symbol 代码:
1 | //at.alicdn.com/t/font_8d5l8fzk5b87iudi.js |
第二步:加入通用 css 代码(引入一次就行):
1 | <style type="text/css"> |
第三步:挑选相应图标并获取类名,应用于页面:
1 | <svg class="icon" aria-hidden="true"> |
Svg Icon
因为 IconFont 在显示图标方面的缺陷,开发者开始使用 SVG 作为其替代方案展示 Icon。
其中 Inline SVG vs Icon Fonts 一文中给出了详细的 Inline Svg 与 Icon Fonts之间的区别。
Iconfont主要的缺陷:
浏览器将其视为文字进行抗锯齿优化,不同系统下对文字的渲染显示效果可能不同
Icon 作为字体进行显示时,其显示的大小、位置都可能会受到
font-size
,line-height
,word-spacing
等css属性影响,其容器的css样式也会可能影响到该字体icon的位置等。Iconfont 仅仅支持单色,且高分辨率下的显示效果不佳。
下面列举了项目中使用 SVG 的几种方式,各有优缺点:
Img/object 标签
早期使用 svg 的一种方式。缺点在于每个图标都需要保存成一个 svg 文件,使用时单独请求。项目中图标过多的化会带来过多的 http 请求。
Inline svg
顾名思义,将 svg 直接写进 html,这种方法简单暴力,可以减少 http 的请求。
优点: 可以直接使用 class 进行 svg 的样式定制,可控性强
缺点: 复用性差,效率低
Data URIs
css 中直接使用 base64 编码后的 svg
1 | .icon{ |
优点: 不需要额外引用 SVG 文件
缺点:可控性差,无法使用 css 进行样式定制,可能会存在潜在的效率问题
svg sprite
初期最基础的 svg sprite 技术类似于 css sprite,通过background-position
等属性控制其显示的位置。类似于css sprite, 社区中也出现了对应的工具和在线网站提供生成 svg sprite 的方法。
优点: 减少 http 请求,可以 fallback 到 css sprite
缺点: 可控性差,无法方便的通过css进行控制样式
基于svg symbols 的svg sprite
svg symbols 是定义 svg 引用的一种方式,基于该方式下的 svg sprite 是在传统 svg sprite 上的改进,改进了在 sprite 中获取单一 Icon 的调用方式,之前是根据位置,基于 svg symbol 下的调用是根据引用。
1 | <svg xmlns="http://www.w3.org/2000/svg" style="display: none;"> |
每个Symbol设置一个id作为其引用的名字。
使用方法:
第一步: 将上述 svg 作为 body 的第一个子元素插入。
第二步: 在需要引用 icon 的地方通过 use xlink:href 的方式使用 svg
1 | <svg class="icon"> |
上述基于 Symbol 制作 svg-sprite 的方式,使用起来方便,通过使用 id 引用对应的svg,避免了使用background-position
进行 svg 的引用。即使后期需要重新合并新的 svg-sprite,只需要合并前后对应svg的symbol id 不发生变化,合并前后业务中已经使用的 svg 就不用做任何变化。
基于 svg symbol 的 svg sprite Icon 展示方案如何集成到 webpack 工作流中
在 vue-svg-icon 这个 demo 中详细给出了基于 webpack 的 vue 单页面项目中如何继承 svg-icon 方案的步骤。
第一步:制作svg-sprite:
webpack 中添加 svg-sprite-loader, 并添加如下配置:
1 |
|
svg-sprite-loader 将我们引用的指定文件夹下的 svg 制作成 svg sprite 并插入 html 的 body 中。
需要注意,此处我们应指定文件夹存放我们项目中所有 svg icon,vue-cli 中还提供了 url-loader 处理 svg,因此我们应添加如下配置,避免 icon 下的 svg 文件被 url-loader 处理。
1 | { |
通过 exclude 的配置可以避免指定icon文件夹下的svg文件被url-loader处理。
第二步: 封装使用时的 svg component
上面给出了在生成 svg sprite 后,通过 use 使用 svg 的方法。为方便项目中引用,封装 SvgIcon.vue 组件。
1 | <!-- svg-icon 组件,业务组件中直接使用该组件展示 icon --> |
使用svg sprite 可能会遇到的问题
svg文件精简的问题
为什么要进行 svg 文件精简?
UI同学通过工具导出的SVG文件通常包含大量冗余且无用的信息,如编辑器元数据,注释,隐藏元素,默认值以及其他可以删除且不影响svg正常渲染的内容。
类似iconmoon.io、Iconfont 都提供了在线精简svg的功能。
在多人协作以及需要频繁改动svg文件的中大型项目中,显然这种依赖手动流程去精简svg的方法已经无法满足快速开发的需要。因此我们需要在我们的工作流中集成类似的精简svg的工具。
svgo(svg optimizer) 是一个基于Nodejs的svg文件优化工具,其通过一系列的配置项可以实现定制化的精简svg的需求。
svgo-loader 基于webpack以及svgo的用于优化svg的loader。
使用方式:
webpack.base.conf.js
1 | // 引入svgo的配置文件 |
svgo-config.json(定义了精简svg的规则)
1 | { |
状态相关的svg-icon以及多色问题的思考
在 web 开发的过程中,我们经常会遇到一些状态相关的 icon。比如,当用户 click 或者 hover 时,我们需要对 icon 的颜色进行相应的变化。采用 Iconfont 方案时,因为其字体的本质,我们可以直接对字体的颜色使用 css 进行控制。
svg-icon 方案的使用过程中 svg 的颜色是一种填充色机制。通过fill属性将具体的路径进行颜色填充。如相关路径未指定fill属性,则其继承父元素的color属性进行填充。
因此 svg-icon 方案下状态颜色的变化分为以下两种情况:
- 多色 svg-icon 的状态变化非单一 path 的颜色变化,状态变化时,直接替换不同状态下的 icon
- 多色 svg-icon 的状态变化仅仅涉及单一 path 下的颜色变化,此时可以通过该 path 的 fill 属性留空,通过css修改其父元素的 color 属性,达到修改 icon 颜色的目的。
总结
基于 svg-sprite 的svg-icon 方案在 14年的时候就已经出现,鉴于当时浏览器兼容性等原因,并没有得到大规模采用。如今随着技术的更新,兼容性显然已经不在是svg-icon应用的阻碍。移动端android 3.x, IE 9+ 都可以进行采用 svg-icon 的 icon 方案。通过调研,发现目前已经应用了 svg-icon 技术方案的有:
- Github 全站采用了svg作为其icon展示方案, inline svg 的使用方式
- iconfont 主站采用了inine svg 的方式,展示其所有的icon
- 京东 部分icon (话费、机票等)采用了基于svg symbol的svg sprite 方案,展示icon
- 腾讯视频 部分icon(暂停、播放)采用了基于svg symbol的svg sprite 方案
任何技术方案的讨论都脱离不了其应用场景。在实际的开发中,因为各种原因,可能会存在多种icon方案并行的情况,因此在实际的开发过程中应具体问题具体分析。
参考链接:
- 手摸手,带你优雅的使用 icon
- Web中的图标
- 未来必热:SVG Sprite技术介绍
- CSS Sprites: What They Are, Why They’re Cool, and How To Use Them
- Data URIs
- 拥抱Web设计新趋势:SVG Sprites实践应用
- Inline SVG vs Icon Fonts
- Web 设计新趋势: 使用 SVG 代替 Web Icon Font
- HOW TO CREATE AND MANAGE SVG SPRITES
- webpack 插件 svg-sprite-loader
- 使用SVG Sprite替代iconfont
- SVG
symbol
a Good Choice for Icons - SVG精简压缩工具svgo简介和初体验
Web Icon 实现方案总结